#pragma once
// for reading the following config.
/*
 // config:

	<main>
		a = 1
		<application>
		  a = 1
		  <me>
			a = 1
		  </me>
		</application>
	</main>

// parse and read like:
		CConfig conf;
		if (conf.Parse("./conf.conf")) {
		    auto a = conf.GetNode<int>("Main/a");
			auto a = conf.GetNode<bool>("Main/application/me/a");
		}
*/
#include <string>
#include <map>
#include <vector>
#include <utility>
#include <stack>
#include "util/defer.hpp"

namespace anet {
	namespace utils {
		// node information.
		struct SNodeInfo {
			// node name.
			std::string name;

			// node key to value lists.
			std::map<std::string, std::string> key2Value;

			// all children lists.
			std::map<std::string, SNodeInfo*> name2Children;
		};

        // node pair.
		typedef std::pair<std::string, bool> nodePair;

		// CConfig class.
		class CConfig final {
		public:
			CConfig();
			virtual ~CConfig();

		public:
			// parse from file.
			bool Parse(const std::string& filePath);
			
			// parse from text, and with sep seperation(such as \n)
			bool ParseContent(const std::string& content, const char *sep = "\n");

			// path is like a/b/c/d.
			nodePair GetNode(const std::string& path);

			// template of GetNode value of T.
			template <typename T>
			T GetNode(const std::string& path, T defaultVal) {
				auto retPair = this->GetNode(path);
				if (!retPair.second) {
					return defaultVal;
				} else {
					return toUserType<T>(retPair.first);
				}
			}

			// return last error.
			std::string LastError();

		protected:
			// parse line of strline.
		    bool parseLine(std::stack<SNodeInfo*> &stk, const std::string &strline, int &line, CDefer &defer);

			// all integral type such as int, char, short and so on.
			template <typename T>
			static typename std::enable_if<std::is_integral<T>::value, T>::type
				toUserType(const std::string& strVal) {
				return std::stoll(strVal);
			}

			// bool template.
			template <>
			static bool toUserType<bool>(const std::string& strVal) {
				if (strVal.size() > 0) {
					switch (strVal[0]) {
					case 't': case 'T':
						return true;
					case 'f': case 'F':
						return false;
					default:
						return std::stoi(strVal) > 0;
					}
				} else {
					return false;
				}
			}

			// unsigned long long.
			template <>
			static unsigned long long toUserType<unsigned long long>(const std::string& strVal) {
				return std::stoull(strVal);
			}

			// float type.
			template <typename T>
			static typename std::enable_if<std::is_same<T, float>::value, T>::type
				toUserType(const std::string& strVal) {
				return std::stof(strVal);
			}

			// double type.
			template <typename T>
			static typename std::enable_if<std::is_same<T, double>::value, T>::type
				toUserType(const std::string& strVal) {
				return std::stod(strVal);
			}

			// std::string
			template <typename T>
			static typename std::enable_if<std::is_same<T, std::string>::value, T>::type
				toUserType(const std::string& strVal) {
				return strVal;
			}

		private:
			nodePair find(std::vector<std::string>& vec, const SNodeInfo* node);
			void freeNode(SNodeInfo* node);

		protected:
			// all nodes.
			SNodeInfo* m_root;
			// error string.
			char       m_error[128];
		};
	}
}